﻿using Abp.Application.Services;
using Abp.BackgroundJobs;
using Abp.Domain.Repositories;
using Alipay.AopSdk.AspnetCore;
using Alipay.AopSdk.Core;
using Alipay.AopSdk.Core.Domain;
using Alipay.AopSdk.Core.Request;
using Alipay.AopSdk.Core.Response;
using FwProject.BackGround.AppShopPay;
using FwProject.Comm;
using FwProject.PublicService.WxPay;
using FwProject.ShoppingMall.Com;
using FwProject.WXMiddleware;
using Microsoft.AspNetCore.Http;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.TenPayLib;
using Senparc.Weixin.MP.TenPayLibV3;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using Castle.Windsor;
using Castle.Core.Logging;
using System.IO;
using Senparc.CO2NET.HttpUtility;
using Senparc.CO2NET;
using FwProject.WxPay;
using Senparc.CO2NET.Helpers;
using System.Collections;
using Alipay.AopSdk.Core.Util;
using Abp.Runtime.Caching;
using Abp.UI;
using FwProject.ManagerNotification;
using System.Threading.Tasks;
using Abp.Threading;
using FwProject.ManagerNotification.DataDto;
using FwProject.OrderDecorator;

namespace FwProject.Pay
{
    public class PayPayAppService : ApplicationService, IPayPayAppService
    {
        private readonly IAlipayService _alipayService;
        private readonly IBackgroundJobManager _backgroundJobManager;
        private readonly IRepository<ShopOrder, Guid> _shopOrderRepository;
        public ILogger logger { get; set; }
        private readonly IWxChartService _wxChartService;
        private IHttpContextAccessor _accessor;
        private readonly ICacheManager _catchmanager;

        private readonly PublishNotifications _publishNotifications;
        public PayPayAppService(ICacheManager catchmanager,IAlipayService alipayService, IWxChartService wxChartService, IHttpContextAccessor accessor,IBackgroundJobManager backgroundJobManager, IRepository<ShopOrder, Guid> shopOrderRepository, PublishNotifications publishNotifications) {
            _accessor = accessor;
            _wxChartService =wxChartService;
            _alipayService = alipayService;
            _backgroundJobManager = backgroundJobManager;
            _shopOrderRepository = shopOrderRepository;
            logger = NullLogger.Instance;
            _publishNotifications = publishNotifications;
            _catchmanager = catchmanager;
        }



        /// <summary>
        /// 获取支付对象值
        /// </summary>
        /// <param name="payData"></param>
        /// <param name="payType"></param>
        /// <returns></returns>
        public string PayString(PayData payData,PayType payType)
        {
            string payString = string.Empty;
            switch (payType)
            {
                case PayType.微信支付:
                    payString=WxChart(payData);
                    break;
                case PayType.支付宝支付:
                    payString = Aliapy(payData);
                    break;
                default:
                    break;
            }
            return payString;
        }


        public bool PayPayNotify(PayType payType)
        {
            bool flag = false;
            switch (payType)
            {
                case PayType.微信支付:
                    flag= WeixinCheckPayNotifyUrl();
                    break;
                case PayType.支付宝支付:
                    flag = AlipayCheckPay();
                    break;
                default:
                    break;
            }
            return flag;
        }

        #region 支付组装
        /// <summary>
        /// 阿里支付参数组装
        /// </summary>
        /// <returns>阿里支付字符</returns>
        private string Aliapy(PayData payData)
        {
            string payStr = string.Empty;
            try
            {
                DefaultAopClient client = new DefaultAopClient(_alipayService.Options.Gatewayurl, _alipayService.Options.AppId, _alipayService.Options.PrivateKey, "json", "1.0",
                          _alipayService.Options.SignType, _alipayService.Options.AlipayPublicKey, _alipayService.Options.CharSet, false);
                // 组装业务参数model
                AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
                //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
                AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
                model.Body = payData.Body;
                model.Subject = payData.Subject;
                model.TotalAmount = $"{payData.PayTotal}";
                model.ProductCode = "QUICK_MSECURITY_PAY";
                model.OutTradeNo = $"{payData.OrderNumber}";
                model.TimeoutExpress = "1d";
                request.SetBizModel(model);
                request.SetNotifyUrl($"{payData.CallBackNotify_url}");
                //这里和普通的接口调用不同，使用的是sdkExecute
                AlipayTradeAppPayResponse response = client.SdkExecute(request);

                payStr= response.Body;
                logger.Info($"订单支付支付宝：订单编号{payData.OrderNumber}支付字符串{payStr}");
            }
            catch (Exception  exe)
            {

                logger.Error($"订单支付支付宝：订单编号{payData.OrderNumber}支付字符串{payStr}",exe);
            }

            return payStr;
        }

        private static string ReurnPayApiUrl(string urlFormat)
        {
            return string.Format(urlFormat, Senparc.Weixin.Config.UseSandBoxPay ? "sandboxnew/" : "");
        }
        /// <summary>
        /// 微信支付参数组装
        /// </summary>
        /// <param name="payData"></param>
        /// <returns>微信支付字符串</returns>
        private string WxChart(PayData payData) {
            string payStr = string.Empty;
            try
            {
                //返回给微信的请求
                Senparc.Weixin.MP.TenPayLibV3.RequestHandler res = new Senparc.Weixin.MP.TenPayLibV3.RequestHandler(_accessor.HttpContext);
                var nonceStr = TenPayV3Util.GetNoncestr();
                TenPayV3UnifiedorderRequestData tenPayV3UnifiedorderRequestData = new TenPayV3UnifiedorderRequestData(
                    appId: _wxChartService.Options.AppId,
                    mchId: _wxChartService.Options.Mch_id,
                    body: payData.Body,
                    outTradeNo: payData.OrderNumber,
                    totalFee: Convert.ToInt16(payData.PayTotal * 100),
                    spbillCreateIp: "47.104.208.31",
                    notifyUrl: payData.CallBackNotify_url,
                    tradeType: TenPayV3Type.APP,
                    openid:"",
                    key: _wxChartService.Options.PrivateKey,
                    nonceStr: nonceStr
                    );
            
                var urlFormat = ReurnPayApiUrl("https://api.mch.weixin.qq.com/{0}pay/unifiedorder");
                var data = tenPayV3UnifiedorderRequestData.PackageRequestHandlerParsXML(_accessor.HttpContext).ParseXML();//获取XML                                                 //throw new Exception(data.HtmlEncode());
                MemoryStream ms = new MemoryStream();
                var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
                ms.Write(formDataBytes, 0, formDataBytes.Length);
                ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
                var resultXml = RequestUtility.HttpPost(urlFormat, null, ms, timeOut:Config.TIME_OUT);


                var timspan =TimeHelp.GetTimestamp();
                //获取并返回预支付XML信息
                var result = new UnifiedorderResult(resultXml);// TenPayV3.Unifiedorder(tenPayV3UnifiedorderRequestData, 10000);



                if (result.prepay_id != null)
                {
                    res.SetParameter("appid", result.appid);
                    res.SetParameter("partnerid", result.mch_id);
                    res.SetParameter("noncestr", result.nonce_str);
                    res.SetParameter("prepayid", result.prepay_id);
                    res.SetParameter("timestamp", timspan);
                    res.SetParameter("package", @"Sign=WXPay");
                    res.SetParameter("key", _wxChartService.Options.PrivateKey);
                    string nativeReqSign = res.CreateMd5Sign("key", _wxChartService.Options.PrivateKey);
                    payStr = res.ParseXML();
                    logger.Info($"订单支付微信：订单编号{payData.OrderNumber}支付字符串{payStr}");
                    var t = new WxPayData
                    {
                        sign = nativeReqSign,
                        appid = result.appid,
                        partnerId = _wxChartService.Options.Mch_id,
                        prepayId = result.prepay_id,
                        package = @"Sign=WXPay",
                        nonceStr = result.nonce_str,
                        timeStamp = timspan
                    };
                    payStr = Newtonsoft.Json.JsonConvert.SerializeObject(t);

               
                }
                else {

                   var OutTradeNo = payData.OrderNumber+DateTime.Now.ToString("HHmmss");
                  tenPayV3UnifiedorderRequestData = new TenPayV3UnifiedorderRequestData(
                  appId: _wxChartService.Options.AppId,
                  mchId: _wxChartService.Options.Mch_id,
                  body: payData.Body,
                  outTradeNo: OutTradeNo,
                  totalFee: Convert.ToInt16(payData.PayTotal * 100),
                  spbillCreateIp: "47.104.208.31",
                  notifyUrl: payData.CallBackNotify_url,
                  tradeType: TenPayV3Type.APP,
                  openid: "",
                  key: _wxChartService.Options.PrivateKey,
                  nonceStr: nonceStr
                  );


                     data = tenPayV3UnifiedorderRequestData.PackageRequestHandlerParsXML(_accessor.HttpContext).ParseXML();//获取XML   

                    ms = new MemoryStream();
                    formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
                    ms.Write(formDataBytes, 0, formDataBytes.Length);
                    ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置

                    var sresultXml = RequestUtility.HttpPost(urlFormat, null, ms, timeOut: Config.TIME_OUT);
                    var  sresult = new UnifiedorderResult(sresultXml);// TenPayV3.Unifiedorder(tenPayV3UnifiedorderRequestData, 10000);
                    Senparc.Weixin.MP.TenPayLibV3.RequestHandler sres = new Senparc.Weixin.MP.TenPayLibV3.RequestHandler(_accessor.HttpContext);
                    sres.SetParameter("appid", sresult.appid);
                    sres.SetParameter("partnerid", sresult.mch_id);
                    sres.SetParameter("noncestr", sresult.nonce_str);
                    sres.SetParameter("prepayid", sresult.prepay_id);
                    sres.SetParameter("timestamp", timspan);
                    sres.SetParameter("package", @"Sign=WXPay");
                    sres.SetParameter("key", _wxChartService.Options.PrivateKey);
                        string nativeReqSign = sres.CreateMd5Sign("key", _wxChartService.Options.PrivateKey);
                        payStr = sres.ParseXML();
                        logger.Info($"订单支付微信：订单编号{payData.OrderNumber}支付字符串{payStr}");
                        var t = new WxPayData
                        {
                            sign = nativeReqSign,
                            appid = sresult.appid,
                            partnerId = _wxChartService.Options.Mch_id,
                            prepayId = sresult.prepay_id,
                            package = @"Sign=WXPay",
                            nonceStr = sresult.nonce_str,
                            timeStamp = timspan
                        };
                    if (sresult.prepay_id == null)
                        new UserFriendlyException("系统繁忙稍后在试");


                    payStr = Newtonsoft.Json.JsonConvert.SerializeObject(t);


                }

               
            }
            catch (Exception exe)
            {

                logger.Error($"订单支付微信：订单编号{payData.OrderNumber}支付字符串{payStr}", exe);
            }

            return payStr;
        }
        #endregion
        /// <summary>
        /// 此接口已经作废
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [Obsolete]
        public bool AlipayCheckPay(Dictionary<string, string> data)
        {
            return false;
        }
        #region 支付验证
        /// <summary>
        /// 支付宝回调通知接口
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private bool AlipayCheckPay() {
            ///调试时使用数据
            ///
            Dictionary<string, string> data = GetRequestPost();
            string orderNum = data["out_trade_no"];
            string trade_no =  data["trade_no"];
#if DEBUG
            logger.Info($"订单支付微信订单编号{Newtonsoft.Json.JsonConvert.SerializeObject(data)}支付返回字符串");
            logger.Info($"眼前字符串{GetSignContent(data)}支付返回字符串");

            #endif
            var flag = _alipayService.RSACheckV1(data);
            logger.Info($"支付宝验证签名结果{flag}支付宝验证签名结果");
            if (flag){
                if (data["trade_status"] == "TRADE_SUCCESS")
                {
                    if (!SetPay(orderNum, trade_no, PayType.支付宝支付))
                        flag = false;
                }
                else
                {
                    flag = false;
                }
                  
                   

            }
            return flag;
        }
       

        /// <summary>
        /// 测试支付接口 如果是微信支付请添加weixinkey参数
        /// </summary>
        /// <param name="payType"></param>
        /// <param name="orderNum"></param>
        /// <param name="trade_no"></param>
        /// <param name="WeiKey"></param>
        /// <returns></returns>
        public bool TestAlipayCheckPay(PayType payType, string orderNum) {

            bool flag = true;
            try
            {
                if (payType == PayType.微信支付)
                {
                    flag = SetPay(orderNum, DateTime.Now.ToString("yyyyMMddhhmmss"), PayType.微信支付);
                }
                else
                {
                    Dictionary<string, string> data = new Dictionary<string, string>();
                    data.Add("out_trade_no", orderNum);
                    data.Add("trade_no", DateTime.Now.ToString("yyyyMMddhhmmss"));
                    flag = SetPay(orderNum,DateTime.Now.ToString("yyyyMMddhhmmss"), PayType.支付宝支付);
                }
            }
            catch (Exception exe)
            {

                Logger.Error($"测试支付修改失败，微信支付成功：订单编号：{orderNum}", exe);
            }
          
            return flag;
        }

        /// <summary>
        /// 微信回调通知支付
        /// </summary>
        /// <returns></returns>
        private bool WeixinCheckPayNotifyUrl()
        {
            bool flage =true ;
            flage= WxPayV3.ProcessNotify(_wxChartService.Options.PrivateKey, delegate (WxPayV3.NotyfyResult res)
            {
                Logger.Info($"NotifyUrl:{res.Content}PAYDATA_wx");
                //TODO:回调成功时处理
                decimal tfee = 0;
                decimal.TryParse(res.total_fee, out tfee);
                //判断订单号是否重复
                var wxorder = res.out_trade_no.Length > 25 ? res.out_trade_no.Substring(0, 25) : res.out_trade_no;
                flage = SetPay(wxorder, res.transaction_id,PayType.微信支付);

                ICache cache = _catchmanager.GetCache(nameof(PayData));
                cache.Remove(res.out_trade_no);

            }, delegate (WxPayV3.NotyfyResult res)
            {
                //TODO:回调失败时处理
                Logger.Info($"NotifyUrl:处理失败=PAYDATA_wx");
            },_accessor.HttpContext)== "success"?true:false;
            //此处一定要返回，不然微信服务器收不到确认信息
            return flage;
        }
        #endregion
        #region 支付通知
        /// <summary>
        /// 设置支付成功后的数据处理
        /// </summary>
        /// <param name="data"></param>
        private bool SetPay(string orderNum, string trade_no, PayType payType) {

            bool flage = true;
            //string orderNum = orderNum;// data["out_trade_no"];
            //string trade_no = trade_no;// data["trade_no"];
            Logger.Info($"{payType}支付成功：订单编号：{orderNum}支付交易编号：{trade_no}");
            try
            {
                var model = _shopOrderRepository.GetAll().Where(o => o.OrderNumber == orderNum).FirstOrDefault();
                model.PayStatus = PayStatus.已支付;
                model.PayType = payType;
                model.alipayTradeNo = trade_no;
                model.PayTime= DateTime.Now;

                PaySuccessData paySuccessData = new PaySuccessData();
                paySuccessData.UserId = model.UserId;
                paySuccessData.PayStatus = PayStatus.已支付;
                paySuccessData.PayType = payType;
                paySuccessData.TradeNo = trade_no;
                paySuccessData.PayData = new PayData { OrderNumber = model.OrderNumber, PayTotal = model.PayTotal, Id = model.Id };
                paySuccessData.PayTime = model.PayTime.Value;

                OrderOperationNotificationData orderOperationNotificationData = new OrderOperationNotificationData(NotificationType.支付成功, NotificationMessage.PaySuccess, model.Id.ToString(), null);
                _backgroundJobManager.Enqueue<PayNotifyBackground, PaySuccessData>(paySuccessData, BackgroundJobPriority.Normal, TimeSpan.FromSeconds(2));
            }
            catch (Exception e)
            {
                flage = false;
                Logger.Error($"支付修改失败，但是{payType}支付成功：订单编号：{orderNum}支付交易编号：{trade_no}",e);
              
            }

            return flage;
        }
        #endregion
        private Dictionary<string, string> GetRequestPost()
        {
            int i = 0;
            Dictionary<string, string> sArray = new Dictionary<string, string>();
            // NameValueCollection coll;
            //Load Form variables into NameValueCollection variable.

            // Get names of all forms into a string array.
            String[] requestItem = _accessor.HttpContext.Request.Form.Keys.ToArray();

            for (i = 0; i < requestItem.Length; i++)
            {
                sArray.Add(requestItem[i], _accessor.HttpContext.Request.Form[requestItem[i]]);
            }

            return sArray;
        }

        private static string GetSignContent(IDictionary<string, string> parameters)
        {
            // 第一步：把字典按Key的字母顺序排序
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
            var dem = sortedParams.GetEnumerator();

            // 第二步：把所有参数名和参数值串在一起
            var query = new StringBuilder("");
            while (dem.MoveNext())
            {
                var key = dem.Current.Key;
                var value = dem.Current.Value;
                if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
                    query.Append(key).Append("=").Append(value).Append("&");
            }
            var content = query.ToString().Substring(0, query.Length - 1);

            return content;
        }
        /// <summary>
        /// 设置微信的支付成功
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [Obsolete]
        private bool SetWeixinPay(string orderNum, string trade_no)
        {
            bool flage = true;

            Logger.Info($"微信支付成功：订单编号：{orderNum}支付宝交易编号：{trade_no}");
            try
            {
                var model = _shopOrderRepository.GetAll().Where(o => o.OrderNumber == orderNum).FirstOrDefault();
                model.PayStatus = PayStatus.已支付;
                model.PayType = PayType.微信支付;
                model.alipayTradeNo = trade_no;
                model.PayTime = DateTime.Now;

                PaySuccessData paySuccessData = new PaySuccessData();
                paySuccessData.UserId = model.UserId;
                paySuccessData.PayStatus = PayStatus.已支付;
                paySuccessData.PayType = PayType.微信支付;
                paySuccessData.TradeNo = trade_no;
                paySuccessData.PayData = new PayData { OrderNumber = model.OrderNumber, PayTotal = model.PayTotal, Id = model.Id };
                paySuccessData.PayTime = model.PayTime.Value;

#if DEBUG
                ///留下很多年以后获取会游泳装饰器对订单各种流出单独去处理
                OrderBaseAction<PaySuccessData> baseaction = new OrderSendAction<PaySuccessData>(_backgroundJobManager, _publishNotifications);
                baseaction = new OrderActionRectangle<PaySuccessData>(_backgroundJobManager, _publishNotifications, baseaction);
                baseaction.Run(paySuccessData);
#endif

                OrderOperationNotificationData orderOperationNotificationData = new OrderOperationNotificationData(NotificationType.支付成功, NotificationMessage.PaySuccess, model.Id.ToString(), null);
                AsyncHelper.RunSync(() => _publishNotifications.Publish_OrderOperation(orderOperationNotificationData));
                _backgroundJobManager.Enqueue<PayNotifyBackground, PaySuccessData>(paySuccessData, BackgroundJobPriority.Normal, TimeSpan.FromSeconds(2));
            }
            catch (Exception e)
            {
                flage = false;
                Logger.Error($"支付修改失败，微信支付成功：订单编号：{orderNum}支付宝交易编号：{trade_no}", e);

            }

            return flage;
        }
    }
}
